package com.google.code.lightsomy.route;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;

import com.google.code.lightsomy.http.LightsomyConstants;

/**
 * Default mapper to map url request with configured routes.
 * 
 * @author Viktoras Agejevas
 *
 */
public class LightsomyMapper implements Mapper {

	private List<Route> routes = new ArrayList<Route>();
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public Mapper addRoute(Route route) {
		routes.add(route);
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Mapper removeRoute(Route route) {
		routes.remove(route);
		return this;
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<Route> getRoutes() {
		return new ArrayList<Route>(routes);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void addAllRoutes(List<Route> routes) {
		this.routes.addAll(routes);
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean match(String url) {
		for (Route route : routes) {
			Matcher m = route.getPattern().matcher(url);
			
			if (m.find()) {
				return true;
			}
		}
		return false;
	}
	
	protected Route findRoute(String url) {
		for (Route route : routes) {
			Matcher m = route.getPattern().matcher(url);
			
			if (m.find()) {
				return route;
			}
		}
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Map<String, String> parse(String url) {
		Route route = findRoute(url);
		
		if (route == null) {
			return null;
		}
		
		Map<String, String> params = new HashMap<String, String>();
		Matcher m = route.getPattern().matcher(url);

		if (route.getAction() != null) {
			params.put(LightsomyConstants.ACTION.getValue(), route.getAction());
		} else {
			params.put(LightsomyConstants.ACTION.getValue(), 
					LightsomyConstants.INDEX_ACTION.getValue());
		}
		
		if (route.getController() != null) {
			params.put(LightsomyConstants.CONTROLLER.getValue(), route.getController());
		} else {
			params.put(LightsomyConstants.CONTROLLER.getValue(), 
					LightsomyConstants.INDEX_CONTROLLER.getValue());
		}
		
		if (m.matches()) {
			List<Integer> groupNumbers = new ArrayList<Integer>(route.getParams().values());
			Collections.sort(groupNumbers);
			if (groupNumbers.size() > 0 && 
					m.groupCount() < groupNumbers.get(groupNumbers.size() - 1)) {
				throw new IllegalStateException("Route parameters don't match with URL");
			}
			
			for (Entry<String, Integer> p : route.getParams().entrySet()) {
				params.put(p.getKey(), m.group(p.getValue()));
			}
		}

		// force the first char of controller to be upper case
		String c = params.get(LightsomyConstants.CONTROLLER.getValue());
		if (c != null) {
			c = Character.toString(c.charAt(0)).toUpperCase() + c.substring(1);
			params.put(LightsomyConstants.CONTROLLER.getValue(), c);
		}
		
		return params;
	}
}
